﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.IO;
using System.Threading;
using UnityEngine;
using Frankfort.Threading.Internal;
using Newtonsoft.Json;

public class RequestState
{
    // This class stores the State of the request.
    const int BUFFER_SIZE = 1024;
//    public StringBuilder requestData;
	public MemoryStream data;
    public byte[] BufferRead;
    public HttpWebRequest request;
    public HttpWebResponse response;
    public Stream streamResponse;
    public RequestState()
    {
        BufferRead = new byte[BUFFER_SIZE];
		data = new MemoryStream();
//        requestData = new StringBuilder("");
        request = null;
        streamResponse = null;
    }
}

public class HttpRequest
{
//    public delegate void HttpRequestCallback(object request, object response, byte[] receiveData, WebExceptionStatus type);
//    public delegate object HttpRequestParseDataCallback(object request, byte[] receiveData);

//    public TrackingStatsType trackType;
    public bool notifyResponse = false;
    private int _retryCount = 1; //访问失败，重试次数
    private int _count = 1;  //当前正在请求的次数

    private const int DefaultTimeout = 15 * 1000; // 
    private const int BUFFER_SIZE = 1024;

    public static ManualResetEvent allDone = new ManualResetEvent(false);

    private HttpWebRequest _request;
    private IAsyncResult _result;
    private System.Action<object, object, byte[], WebExceptionStatus> _callback;
    private System.Func<object, byte[], object> _parseDataCallback;
    private System.Object lockObject = new System.Object();

    private bool _isCancel = false;

    private Thread _workThread;
    private string _url;
    public string url
    {
        get
        {
            return _url;
        }
    }
    private object _postContent;
    private bool _success = false;
    public bool success
    {
        get
        {
            return _success;
        }
    }

	static int _seq = 0;
	string _cur_seq = "";

	public HttpRequest()
	{
//		DataChangeBroadcast.Instance.Subscribe (DataChangeType.HttpRequestFinished, this.OnHttpRequestFinished);
	}

	~HttpRequest()
	{
//		DataChangeBroadcast.Instance.Unsubscribe (DataChangeType.HttpRequestFinished, this.OnHttpRequestFinished);
	}

    public void Request(string url, object postContent, System.Action<object, object, byte[], WebExceptionStatus> callback, System.Func<object, byte[], object> parseDataCallback, bool start = true, int retryCount = 1)
    {
        _url = url;
        _postContent = postContent;
        _callback = callback;
        _parseDataCallback = parseDataCallback;
        _retryCount = retryCount;

        if (start == false)
        {
            return;
        }

        if (_retryCount > 1)
        {
//            if (Debug.enableLog)
//                Debug.Log("Request trackType: " + trackType + " count: " + _count);
        }


        _workThread = SingleThreadStarter.StartSingleThread(() =>
        {

            try
            {
                MainThreadDispatcher.Init();
                _request = (HttpWebRequest)WebRequest.Create(url);
                
                _request.ReadWriteTimeout = DefaultTimeout;
                _request.Timeout = DefaultTimeout;

                RequestState myRequestState = new RequestState();
                myRequestState.request = _request;

//                Debug.Log("Request Url: " + url);

                if (postContent != null)
                {
                    _request.Method = "POST";

//                    Debug.Log("Post content: " + postContent);

					byte[] b1 = null;
					if(postContent is string)
					{
						b1 = System.Text.Encoding.UTF8.GetBytes(postContent as string);
					}
					else if(postContent is byte[])
					{
						b1 = postContent as byte[];
					}
                    _request.ContentLength = b1.Length;
                    Stream requestStream = _request.GetRequestStream();
                    requestStream.Write(b1, 0, b1.Length);
                    requestStream.Close();
                }

//				Debug.LogError("request begin:" + " url:" + this._url);
                // Start the asynchronous request.
                _result = (IAsyncResult)_request.BeginGetResponse(new AsyncCallback(RespCallback), myRequestState);
            }
            catch (WebException e)
            {
                Debug.Log("Request Exception raised!");
                Debug.Log("Message: " + e.Message);
                Debug.Log("Status: " + e.Status);
                this.ResponseError(e.Status);
            }

        });
        
    }

    public void Start()
    {
        _count = 1;
        this.Retry();
    }

    private void Retry()
    {
        this.Request(_url, _postContent, _callback, _parseDataCallback, true, _retryCount);
    }

    public void Cancel()
    {
        lock(lockObject)
        {
            _isCancel = true;
            if (_workThread != null && _workThread.IsAlive)
            {
                //_workThread.Abort();
                _workThread = null;
            }

            lock (lockObject)
            {
                if (_request != null)
                {
                    _request.Abort();
                    _request = null;
                }
            }
        }
        
    }

    public void TimeOut()
    {
        lock(lockObject)
        {
            if (_request != null)
            {
                _request.Abort();
                _request = null;
            }
            //else
            {
                this.ResponseError(WebExceptionStatus.Timeout);
            }
            
        }
    }

    private void RespCallback(IAsyncResult asynchronousResult)
    {
        try
        {
            // State of request is asynchronous.
            RequestState myRequestState = (RequestState)asynchronousResult.AsyncState;
            HttpWebRequest myHttpWebRequest = myRequestState.request;
            myRequestState.response = (HttpWebResponse)myHttpWebRequest.EndGetResponse(asynchronousResult);

            // Read the response into a Stream object.
            Stream responseStream = myRequestState.response.GetResponseStream();
            myRequestState.streamResponse = responseStream;
			responseStream.ReadTimeout = DefaultTimeout;

            // Begin the Reading of the contents of the HTML page and print it to the console.
            IAsyncResult asynchronousInputRead = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
            return;
        }
        catch (WebException e)
        {
            Debug.Log("RespCallback Exception raised!");
            Debug.Log("Message: " + e.Message);
            Debug.Log("Status: " + e.Status);
            this.ResponseError(e.Status);
        }

    }

    private void ReadCallBack(IAsyncResult asyncResult)
    {
        try
        {
            RequestState myRequestState = (RequestState)asyncResult.AsyncState;
            Stream responseStream = myRequestState.streamResponse;
            int read = responseStream.EndRead(asyncResult);
            // Read the HTML page and then print it to the console.
            if (read > 0)
            {
//				Debug.LogError("readdata begin:" + read + " url:" + this._url);
//                myRequestState.requestData.Append(Encoding.UTF8.GetString(myRequestState.BufferRead, 0, read));
				myRequestState.data.Write(myRequestState.BufferRead, 0, read);
                IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
//				Debug.LogError("readdata end:" + read + " url:" + this._url);
                return;
            }
            else
            {
                if (myRequestState.data.Length > 1)
                {
//                    string stringContent;
//                    stringContent = myRequestState.requestData.ToString();

					byte[] data = myRequestState.data.ToArray();
                    object obj = data;
                    if(this._parseDataCallback != null)
                    {
                        obj = _parseDataCallback(this, data);
                    }
                    else
                    {
                        obj = System.Text.Encoding.UTF8.GetString(data);
                    }

                    this.ResonseSuccess(obj, data);
                }

                responseStream.Close();
            }

        }
        catch (WebException e)
        {
            Debug.Log("ReadCallBack Exception raised!");
            Debug.Log("Message: " + e.Message);
            Debug.Log("Status: " + e.Status);
            this.ResponseError(e.Status);
        }
    }

    private void ResponseError(WebExceptionStatus type)
    {
        //MainThreadDispatcher.
        lock(lockObject)
        {
            if (_request != null)
            {
                _request.Abort();
                _request = null;
            }

            if (_isCancel)
            {
                return;
            }
        }
        
        MainThreadDispatcher.DispatchToMainThread(mainThreadResponseError, type);
    }

    private void mainThreadResponseError(object type)
    {
        Debug.Log("mainThreadResponseError url: " + _url + " count: " + _count + " retryCount: " + _retryCount);
        _count++;
        if (_count <= _retryCount)
        {
            this.Retry();
            return;
        }

        if (_callback != null)
            _callback(this, null, null, (WebExceptionStatus)type);
//        if (notifyResponse)
//            DataChangeBroadcast.Instance.RaiseEvent(DataChangeType.HttpResponse, this);
    }

    private void ResonseSuccess(object obj, byte[] data)
    {
        lock (lockObject)
        {
            if (_request != null)
            {
                _request = null;
            }

            if (_isCancel)
            {
                return;
            }
        }

        MainThreadDispatcher.DispatchToMainThread((object o) =>
		{
                mainThreadResonseSuccess(obj, data);
		}, null);
    }

    private void mainThreadResonseSuccess(object obj, byte[] data)
    {
        _success = true;
        if (_callback != null)
            _callback(this, obj, data, WebExceptionStatus.Success);

//        if (notifyResponse)
//            DataChangeBroadcast.Instance.RaiseEvent(DataChangeType.HttpResponse, this);
//        DataChangeBroadcast.Instance.RaiseEvent(DataChangeType.NetworkAvailable, null);
    }

//	private void OnHttpRequestFinished(DataChangeType type, params object[] objs)
//	{
//		if (objs == null || objs.Length == 0) 
//		{
//			return;
//		}
//
//		string result = (string)objs[0];
//		if (Debug.enableLog) 
//		{
//			Debug.Log("OnHttpRequestFinished: " + result);
//		}
//
//		if (result == null || result.Length == 0) 
//		{
//			return;
//		}
//
//		Dictionary<string, string> dic = JsonConvert.DeserializeObject<Dictionary<string, string>> (result);
//		if (_cur_seq.Equals (dic ["seq"]) == false) 
//		{
//			return;
//		}
//
//		string response = "";
//		if (dic.ContainsKey ("response")) 
//		{
//			response = dic["response"];
//		}
//
//		int success = int.Parse (dic["success"]);
//
//		if (success == 0) 
//		{
//			mainThreadResponseError(WebExceptionStatus.UnknownError);
//		} 
//		else 
//		{
//			mainThreadResonseSuccess(response, null);
//		}
//	}
}
